package xyz.jcat.spring.security.example.config;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import xyz.jcat.spring.security.example.AuthenticationRestHandler;
import xyz.jcat.spring.security.example.UserDetailsServiceImpl;
import xyz.jcat.spring.security.example.filter.ValidateCodeFilter;

import java.util.ArrayList;
import java.util.List;

@Configuration
//开启权限注解支持
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private AuthenticationRestHandler authenticationRestHandler;
    @Autowired
    private ValidateCodeFilter validateCodeFilter;
    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;
    @Autowired
    private AccessDeniedHandler accessDeniedHandler;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    /**
     * http请求设置
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
          //spring security 默认表单页面
//        http.httpBasic()
//            .and()
//                .authorizeRequests()
//                .anyRequest().authenticated()//所有请求都需要认证
//        ;

//        http.formLogin()//开启表单认证
//                .loginPage("/xxx.html")//自定义表单页面;
//                .loginProcessingUrl("/login")//表单提交路径
//                .usernameParameter("username")//表单提交用户名参数名
//                .passwordParameter("password")//表单提交密码参数名
//                .successForwardUrl("/xxx.html")//登录成功跳转页面
//            .and()
//                .authorizeRequests()
//                .antMatchers("xxxUrl").permitAll()//不需要认证请求
//                .anyRequest().authenticated()
//            .and()
//                .csrf().disable() //关闭csrf防护
//        ;
        //认证之前加入验证码过滤器
        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);
        //配置权限
        //固定权限 不适用可配置权限场景
//        List<Permission> permissions = getPermissions();
//        for (Permission permission : permissions) {
//            http.authorizeRequests().mvcMatchers(permission.getMethod(), permission.getUri())
//                    .hasAuthority(permission.getPermission());
//        }

        http.formLogin()
                .loginProcessingUrl("/login")
                .successHandler(authenticationRestHandler)//登录成功处理类
                .failureHandler(authenticationRestHandler)//登录失败处理类
            .and()
                .logout().logoutSuccessHandler(authenticationRestHandler)//退出登录处理类
            .and()
                .authorizeRequests()
                .anyRequest().authenticated()
            .and()
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)//未认证异常处理类
                .accessDeniedHandler(accessDeniedHandler)//未授权异常处理类
            .and()
                .csrf().disable();

        //开启跨域支持
        http.cors().configurationSource(corsConfigurationSource());
    }

    @Getter
    @Setter
    @AllArgsConstructor
    private static class Permission {

        private HttpMethod method;
        private String uri;
        private String permission;

    }

    //TODO 查询数据库
    private List<Permission> getPermissions() {
        List<Permission> permissions = new ArrayList<>();
        permissions.add(new Permission(HttpMethod.GET,"/user", "user:select"));
        permissions.add(new Permission(HttpMethod.POST,"/user", "user:add"));
        permissions.add(new Permission(HttpMethod.DELETE,"/user", "user:del"));
        permissions.add(new Permission(HttpMethod.GET,"/role", "role:select"));
        permissions.add(new Permission(HttpMethod.POST,"/role", "role:add"));
        permissions.add(new Permission(HttpMethod.DELETE,"/role", "role:del"));
        return permissions;
    }

    /**
     * 身份安全管理器
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
//        web.ignoring().antMatchers("/static/xxx");//忽略静态资源认证
    }

    public CorsConfigurationSource corsConfigurationSource(){
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource =
                new UrlBasedCorsConfigurationSource();
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
        return urlBasedCorsConfigurationSource;
    }

}
